﻿#region Header
// Copyright 2007-2011, Jerónimo Milea
// 
// This file is part of NetJsWire.
// 
//     NetJsWire is free software: you can redistribute it and/or modify
//     it under the terms of the GNU General Public License as published by
//     the Free Software Foundation, either version 3 of the License, or
//     (at your option) any later version.
// 
//     NetJsWire is distributed in the hope that it will be useful,
//     but WITHOUT ANY WARRANTY; without even the implied warranty of
//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//     GNU General Public License for more details.
// 
//     You should have received a copy of the GNU General Public License
//     along with NetJsWire.  If not, see <http://www.gnu.org/licenses/>.
#endregion

using System.ComponentModel;
using System.Drawing;
using System.Runtime.Serialization;
using System.Windows.Forms;
using NetJsWire.Lite.Converters;
using NetJsWire.Lite.Properties;
using NetJsWire.Lite.Widgets.UI.Data;
using Newtonsoft.Json;

namespace NetJsWire.Lite.Widgets.UI.Form
{
    public enum TriggerAction
    {
        Query,
        All
    }

    public enum Mode
    {
        Remote,
        Local
    }

    [JsonObject( MemberSerialization = MemberSerialization.OptIn, Id = "Combo" )]
    //[ComplexBindingProperties( "Store" )]
    [ToolboxBitmap( typeof( ComboBoxLW ), "NetJsWire.Design.Images.Resources.bullet_wrench.bmp" )]
    public class ComboBoxLW : TextFieldLW
    {
        private object _oldData;

        public ComboBoxLW()
            : this( null )
        {
        }

        public ComboBoxLW( IContainer container )
            : base( container )
        {
            base.Xtype = "combo";
            Size = new Size( 166, 22 );
            base.Margin = new Padding( 0 );
            SubmitValue = true;
            Editable = true;
        }

        [JsonIgnore]
        [Browsable( false )]
        public override string Text
        {
            get { return base.Text; }
            set { base.Text = value; }
        }

        [Browsable( true )]
        [JsonProperty( "triggerAction", DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore )]
        [JsonConverter( typeof( LowerCaseEnumConverter ) )]
        [DefaultValue( ( TriggerAction ) 0 )]
        [Description( "The action to execute when the trigger is clicked.\n'query' : Default\nrun the query using the raw value.\n'all' :\nrun the query specified by the allQuery config option" )]
        public TriggerAction TriggerAction { get; set; }

        [Browsable( true )]
        [JsonProperty( "mode", DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore )]
        [JsonConverter( typeof( LowerCaseEnumConverter ) )]
        [DefaultValue( ( Mode ) 0 )]
        [Description( "Acceptable values are: 'remote', Automatically loads the store the first time the trigger is clicked. If you do not want the store to be automatically loaded the first time the trigger is clicked, set to 'local' and manually load the store.\n'local' : ComboBox loads local data" )]
        public Mode Mode { get; set; }

        /// <summary>
        ///   The data source to which this combo is bound (defaults to undefined).
        ///   any Store subclass
        ///   an Array : Arrays will be converted to a Ext.data.ArrayStore internally, automatically generating field names to work with all data components.
        ///   1-dimensional array : (e.g., ['Foo','Bar'])
        ///   A 1-dimensional array will automatically be expanded (each array item will be used for both the combo valueField and displayField)
        ///   2-dimensional array : (e.g., [['f','Foo'],['b','Bar']])
        ///   For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo valueField, while the value at index 1 is assumed to be the combo displayField.
        /// </summary>
        [Browsable( true )]
        [JsonProperty( "store", DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore )]
        [DefaultValue( null )]
        //[AttributeProvider( typeof( IStore ) )]
        [Description( "The data source to which this combo is bound (defaults to undefined)." )]
        public Store Store { get; set; }

        [Browsable( true )]
        [JsonIgnore]
        [DefaultValue( ( string ) null )]
        [Description( "Set this if you want to use an existing client store instead of declaring one in the designer." )]
        public string ClientStore { get; set; }

        [Browsable( true )]
        [DefaultValue( ( string ) null )]
        [JsonProperty( "hiddenName", DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore )]
        [Description( "If specified, a hidden form field with this name is dynamically generated to store the field's data value (defaults to the underlying DOM element's name). Required for the combo's value to automatically post during a form submission." )]
        public string HiddenName { get; set; }

        [Browsable( true )]
        [DefaultValue( null )]
        [JsonProperty( "hiddenValue", DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore )]
        [Description( "Sets the initial value of the hidden field if hiddenName is specified to contain the selected valueField, from the Store. Defaults to the configured value." )]
        [TypeConverter( typeof( StringConverter ) )]
        [JsonConverter( typeof( NotQuotedJsonStringConverter ) )]
        public object HiddenValue { get; set; }

        [Browsable( true )]
        [JsonProperty( "valueField", DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore )]
        [DefaultValue( ( string ) null )]
        [Description( "The underlying data value name to bind to this ComboBox (defaults to undefined if mode = 'remote' or 'field2' if transforming a select or if the field name is autogenerated based on the store configuration).\nNote: use of a valueField requires the user to make a selection in order for a value to be mapped. See also hiddenName, hiddenValue, and displayField." )]
        public string ValueField { get; set; }

        [Browsable( true )]
        [JsonProperty( "listWidth", DefaultValueHandling = DefaultValueHandling.Ignore )]
        [DefaultValue( 0 )]
        [Description( "The width (used as a parameter to Ext.Element.setWidth) of the dropdown list (defaults to the width of the ComboBox field).\nLeave at 0 to use combo width." )]
        public int ListWidth { get; set; }

        [Browsable( true )]
        [JsonProperty( "displayField", DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore )]
        [DefaultValue( ( string ) null )]
        [Description( "The underlying data field name to bind to this ComboBox (defaults to undefined if mode = 'remote' or 'field1' if transforming a select or if the field name is autogenerated based on the store configuration). " )]
        public string DisplayField { get; set; }

        [Browsable( true )]
        [JsonProperty( "typeAhead", DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore )]
        [DefaultValue( false )]
        [Description( "true to populate and autoselect the remainder of the text being typed after a configurable delay (typeAheadDelay) if it matches a known value (defaults to false)" )]
        public bool TypeAhead { get; set; }

        [Browsable( true )]
        [JsonProperty( "editable", DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore )]
        [DefaultValue( true )]
        [Description( "false to prevent the user from typing text directly into the field, the field will only respond to a click on the trigger to set the value. (defaults to true)." )]
        public bool Editable { get; set; }

        [Browsable( true )]
        [JsonProperty( "submitValue", DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore )]
        [DefaultValue( true )]
        [Description( "False to clear the name attribute on the field so that it is not submitted during a form post. If a hiddenName is specified, setting this to true will cause both the hidden field and the element to be submitted." )]
        public bool SubmitValue { get; set; }

        [Browsable( false ), EditorBrowsable( EditorBrowsableState.Never )]
        [JsonProperty( "___store", DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore )]
        [DefaultValue( null )]
        [JsonConverter( typeof( NotQuotedJsonStringConverter ) )]
        public object InternalDataStore { get; set; }

        [Browsable( true )]
        [JsonProperty( "lazyRender", DefaultValueHandling = DefaultValueHandling.Ignore )]
        [DefaultValue( false )]
        [Description( "true to prevent the ComboBox from rendering until requested (should always be used when rendering into an Ext.Editor (e.g. Grids), defaults to false)." )]
        public virtual bool LazyRender { get; set; }

        public override int Height
        {
            get
            {
                return 22;
            }
            set
            {
                base.Height = 22;
            }
        }

        public bool FormattingEnabled { get; set; }

        internal override void OnPaintInternal( PaintEventArgs e )
        {
            OnPaint( e );
        }

        protected override void OnPaint( PaintEventArgs e )
        {
            using ( var whiteBrush = new SolidBrush( Color.White ) )
            {
                e.Graphics.FillRectangle( whiteBrush, e.ClipRectangle.X, e.ClipRectangle.Y, e.ClipRectangle.Width - 2, e.ClipRectangle.Height - 1 );
            }

            if ( !ReadOnly )
                e.Graphics.DrawImage( Resources.Trigger, e.ClipRectangle.Width - 17, e.ClipRectangle.Y, 17, 21 );

            using ( var pen = new Pen( Color.FromArgb( 181, 184, 200 ) ) )
            {
                e.Graphics.DrawRectangle( pen, e.ClipRectangle.X, e.ClipRectangle.Y, e.ClipRectangle.Width - ( ReadOnly ? 1 : 18 ), e.ClipRectangle.Height - 1 );
                e.Graphics.DrawLine( pen, e.ClipRectangle.X + e.ClipRectangle.Width - ( ReadOnly ? 1 : 18 ), e.ClipRectangle.Y + e.ClipRectangle.Height - 1, e.ClipRectangle.X + e.ClipRectangle.Width, e.ClipRectangle.Y + e.ClipRectangle.Height - 1 );
            }

            var text = Value is string || ( Value != null && !string.IsNullOrWhiteSpace( Value.ToString() ) )
                           ? Value.ToString()
                           : null;

            if ( !string.IsNullOrWhiteSpace( text ) )
            {
                using ( var font = new Font( "Tahoma", 12, FontStyle.Regular, GraphicsUnit.Pixel ) )
                {
                    TextRenderer.DrawText( e.Graphics, text, font,
                                           new Rectangle( 3, 4, ClientSize.Width - 24, ClientSize.Height - 4 ),
                                           Color.Black, Color.White,
                                           TextFormatFlags.TextBoxControl );
                }
            }
        }

        public bool ShouldSerializeWidth()
        {
            return Width != 0 && Width != 166;
        }

        //private object oldStore;
        [OnSerializing]
        internal void OnSerializingMethod( StreamingContext context )
        {
            _oldData = null;
            if ( Store == null && Data != null )
            {
                _oldData = Data;
                if ( Data is string )
                {
                    var parsed = JsonConvert.DeserializeObject( Data.ToString() );
                    InternalDataStore = parsed;
                }
                else
                {
                    InternalDataStore = Data;
                }
                Data = null;
            }
            else if ( Store == null && Data == null && ClientStore != null )
            {
                InternalDataStore = ClientStore;
            }
        }

        [OnSerialized]
        internal void OnSerializedMethod( StreamingContext context )
        {
            Data = _oldData;
            InternalDataStore = null;
        }
    }
}
